﻿using System;
using System.Collections.Generic;

namespace Core
{
    public abstract class Entity : Component, IEntity
    {
        //TODO:考虑使用KeyList替换
        protected readonly Dictionary<Type, List<Component>> Dict = new();

        private bool Check(Type type)
        {
            var isAllowMulti = S.Assembly.IsAllowMulti(type);
            if (isAllowMulti) return true;

            if (Dict.TryGetValue(type, out var value) == false)
            {
                return true;
            }

            return value.Count == 0;
        }

        private void Add(Type type, Component component)
        {
            if (Dict.ContainsKey(type) == false)
            {
                Dict[type] = new List<Component>();
            }

            Dict[type].Add(component);
        }

        public virtual T Add<T>()
            where T : Component
        {
            Type type = typeof(T);

            if (Check(type) == false)
            {
                log.err($"Entity.Add, component already exist and is not allow multi, component.type={type.Name}");
                return null;
            }

            T component = G.Factory.CreateInstance<T>();
            component.SetSG(S, G, this);

            Add(type, component);

            G.Factory.Awake(component);

            return component;
        }

        public virtual T Add<T, A>(A a)
            where T : Component
        {
            Type type = typeof(T);

            if (Check(type) == false)
            {
                log.err($"Entity.Add, component already exist and is not allow multi, component.type={type.Name}");
                return null;
            }

            T component = G.Factory.CreateInstance<T>();
            component.SetSG(S, G, this);

            Add(type, component);

            G.Factory.Awake(component, a);

            return component;
        }

        public virtual T Add<T, A, B>(A a, B b)
            where T : Component
        {
            Type type = typeof(T);

            if (Check(type) == false)
            {
                log.err($"Entity.Add, component already exist and is not allow multi, component.type={type.Name}");
                return null;
            }

            T component = G.Factory.CreateInstance<T>();
            component.SetSG(S, G, this);

            Add(type, component);

            G.Factory.Awake(component, a, b);

            return component;
        }

        public virtual T Add<T, A, B, C>(A a, B b, C c)
            where T : Component
        {
            Type type = typeof(T);

            if (Check(type) == false)
            {
                log.err($"Entity.Add, component already exist and is not allow multi, component.type={type.Name}");
                return null;
            }

            T component = G.Factory.CreateInstance<T>();
            component.SetSG(S, G, this);

            Add(type, component);

            G.Factory.Awake(component, a, b, c);

            return component;
        }

        public virtual void Remove(Component component)
        {
            if (IsDisposed || component == null)
            {
                return;
            }

            Type type = component.GetType();
            if (Dict.TryGetValue(type, out var lst) == false)
            {
                return;
            }

            lst.Remove(component);
            if (lst.Count == 0)
            {
                Dict.Remove(type);
            }

            component.Dispose();
        }

        public void Remove<T>()
        {
            if (IsDisposed) return;

            var type = typeof(T);
            if (Dict.TryGetValue(type, out var lst) == false)
            {
                return;
            }

            foreach (var t in lst)
            {
                t.Dispose();
            }

            lst.Clear();
            Dict.Remove(type);
        }

        /// <summary>
        /// 需要严格匹配类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public List<T> GetAll<T>()
            where T : class
        {
            var t = typeof(T);

            List<T> ll = new();

            if (Dict.TryGetValue(t, out var lst) == false)
            {
                return null;
            }

            foreach (var t1 in lst)
            {
                if (t1 is T obj)
                {
                    ll.Add(obj);
                }
            }

            return ll;
        }

        /// <summary>
        /// 可以获取继承自T的对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Get<T>()
            where T : class
        {
            var t = typeof(T);

            if (!Dict.TryGetValue(t, out var lst)) return GetInherit<T>();

            return lst.Count == 0 ? null : lst[0] as T;
        }

        private T GetInherit<T>()
            where T : class
        {
            foreach (var pair in Dict)
            {
                var lst = pair.Value;
                if (lst.Count == 0) continue;

                if (lst[0] is T t)
                {
                    return t;
                }
            }

            return null;
        }

        public virtual T Create<T>()
            where T : Component
        {
            return G.Factory.CreateWithParent<T>(this);
        }

        public virtual T Create<T, A>(A a)
            where T : Component
        {
            return G.Factory.CreateWithParent<T, A>(this, a);
        }

        public virtual T Create<T, A, B>(A a, B b)
            where T : Component
        {
            return G.Factory.CreateWithParent<T, A, B>(this, a, b);
        }

        public virtual T Create<T, A, B, C>(A a, B b, C c)
            where T : Component
        {
            return G.Factory.CreateWithParent<T, A, B, C>(this, a, b, c);
        }

        public T Create<T, A, B, C, D>(A a, B b, C c, D d)
            where T : Component
        {
            return G.Factory.CreateWithParent<T, A, B, C, D>(this, a, b, c, d);
        }

        public override void Dispose()
        {
            if (IsDisposed) return;

            foreach (var pair in Dict)
            {
                var lst = pair.Value;
                for (int i = 0; i < lst.Count; i++)
                {
                    lst[i].Dispose();
                }

                lst.Clear();
            }

            Dict.Clear();

            base.Dispose();
        }
    }
}
